package com.zhouheng.mongodb.service.impl;

import com.zhouheng.mongodb.entity.*;
import com.zhouheng.mongodb.service.MongoTemplateService;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.GroupOperation;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

/**
 * MongoTemplate服务
 *
 * @author 周恒
 * @date 20200421 10:25:13
 * @since v1.0
 */
@Service
public class MongoTemplateServiceImpl implements MongoTemplateService {

    public MongoTemplateServiceImpl(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    /**
     * mongoTemplate mongoTemplate
     */
    private final MongoTemplate mongoTemplate;

    public static String getMongoSiteInfoCollectionName() {
        return getMongoSiteInfoCollectionName("DERF_DAILY", "PRE", "PCA");
    }

    /**
     * 自定义站点数据的分表策略
     *
     * @param dataType      资料类型
     * @param elementType   要素类型
     * @param algorithmType 算法类型
     * @return 返回 mongo site info collection name
     * @author 周恒
     * @date 20200421 10:37:12
     * @since v1.0
     */
    public static String getMongoSiteInfoCollectionName(String dataType, String elementType, String algorithmType) {
        return dataType + "_" + elementType + "_" + algorithmType + "_SITEINFO";
    }

    /**
     * 自定义格点数据的分表策略
     *
     * @param dataType    描述此参数
     * @param elementType 描述此参数
     * @return 返回 string 描述此返回参数
     * @author 周恒
     * @date 20200421 15:05:12
     * @since v1.0
     */
    public static String getMongoGridGroundCollectionName(String dataType, String elementType){
        return dataType + "_" + elementType +  "_GROUND_GRIDINFO";
    }

    @Override
    public List<BaseSiteInfo> query(){
        List<BaseSiteInfo> baseSiteInfoList = mongoTemplate.find(Query.query(Criteria.where("observeMonthDay").is("0101")
                        .and("observeYear").is(2010)
                        .and("height").is("")), BaseSiteInfo.class,
                getMongoSiteInfoCollectionName("DERF_DAILY", "PRE", "ORIGINAL"));
        return baseSiteInfoList;
    }

    @Override
    public List<ForecastScoreDetail> aggregationAvg(){
        Object[] timeTagArr = Stream.of("2019010120190101", "2018010120180101").toArray();
        Criteria timeCriteria = Criteria.where("timeTag").in(timeTagArr)
                .and("elementType").is("021007")
                .and("timeFlag").is(false)
                .and("stationAreaId").is("1");
        //聚合查询出该月份大于指定的分数资料和算法数据
        TypedAggregation<ForecastScore> aggregation = Aggregation.newAggregation(
                ForecastScore.class,
                match(timeCriteria),
                unwind("scoreList"),
                //这里平均值的as score只是match匹配中要用，而映射到ForecastScoreDetail的字段还是psScore
                group("scoreList.dataType", "scoreList.algorithmType", "scoreList.observeMonthday")
                        .avg("scoreList.psScore").as("score"),
                // 聚合中的分条件
                match(new Criteria().andOperator(Criteria.where("score").gte(60f),
                        Criteria.where("score").lte(80f)))
        );
        AggregationResults<ForecastScoreDetail> results = mongoTemplate.aggregate(aggregation, ForecastScoreDetail.class);
        //分别进行按算法和按资料的概率统计
        return results.getMappedResults();
    }

    @Override
    public long aggregationCount() {
        boolean synchronization = true;
        Object[] timeTagArr = Stream.of("2019010120190101", "2018010120180101").toArray();
        Criteria queryCriteria = Criteria.where("timeTag").in(timeTagArr)
                .and("dataType").is("020005")
                .and("algorithmType").is("022001");
        String[] groupFields = ArrayUtils.toArray("scoreList.dataType", "scoreList.algorithmType", "scoreList.observeMonthday");
        //如果为近期查询，则将起报年加入分组
        if (!synchronization) {
            groupFields = ArrayUtils.add(groupFields, "scoreList.observeYear");
        }
        TypedAggregation<ForecastScore> countAggregation = newAggregation(
                ForecastScore.class,
                unwind("scoreList"),
                match(queryCriteria),
                group(groupFields),
                count().as("total")
        );
        AggregationResults<IntelligenceQueryDto> countResult = mongoTemplate.aggregate(countAggregation, IntelligenceQueryDto.class);
        IntelligenceQueryDto intelligenceQueryDto = countResult.getUniqueMappedResult();
        //如果没有符合条件的数据直接返回
        if (null != intelligenceQueryDto) {
            return intelligenceQueryDto.getTotal();
        }
        return 0L;
    }

    @Override
    public List<ForecastScoreDetail> aggregationQueryPage() {
        boolean synchronization = true;
        Object[] timeTagArr = Stream.of("2019010120190101", "2018010120180101").toArray();
        Criteria queryCriteria = Criteria.where("timeTag").in(timeTagArr)
                .and("dataType").is("020005")
                .and("algorithmType").is("022001");
        String[] groupFields = ArrayUtils.toArray("scoreList.dataType", "scoreList.algorithmType", "scoreList.observeMonthday");
        //如果为近期查询，则将起报年加入分组
        if (!synchronization) {
            groupFields = ArrayUtils.add(groupFields, "scoreList.observeYear");
        }
        Sort.Direction direction = Sort.Direction.DESC;
        long page = 1;
        long size = 10;
        TypedAggregation aggregation = Aggregation.newAggregation(
                ForecastScore.class,
                unwind("scoreList"),
                match(queryCriteria),
                group(groupFields)
                        .avg("scoreList.psScore").as("psScore")
                        .avg("scoreList.ccScore").as("ccScore"),
                sort(direction, "scoreList.psScore"),
                skip(((page - 1) * size)),
                limit(size),
                project("psScore", "ccScore")
        );
        AggregationResults<ForecastScoreDetail> dataResults = mongoTemplate.aggregate(aggregation, ForecastScoreDetail.class);
        List<ForecastScoreDetail> scoreDetails = dataResults.getMappedResults();
        return scoreDetails;
    }

    @Override
    public void remove() {
        mongoTemplate.remove(Query.query(Criteria.where("observeMonthDay").is("0102")
                .and("observeYear").is(2019)
                .and("index").is(1)
                .and("height").is("")), getMongoSiteInfoCollectionName());
    }

    @Override
    public void insert() {
        BaseSiteInfo baseSiteInfo = new BaseSiteInfo();
        //省略具体构建细节
        mongoTemplate.insert(baseSiteInfo, getMongoSiteInfoCollectionName());
    }

    @Override
    public List<BaseSiteInfo> findAndRemove() {
        List<BaseSiteInfo> baseSiteInfoList = mongoTemplate.findAllAndRemove(Query.query(Criteria.where("observeMonthDay").is("1201")
                        .and("observeYear").is(2012)
                        .and("height").is("")), BaseSiteInfo.class,
                getMongoSiteInfoCollectionName("DERF_DAILY", "PRE", "ORIGINAL"));
        return baseSiteInfoList;
    }

    @Override
    public List<GridData> operatorOrAnd() {
        //更复杂的可以使用，如下方式，去拼接
        // new Criteria().andOperator(Criteria.where("score").gte(60f),Criteria.where("score").lte(80f))
        int beginYear = 2019;
        String beginMonthDay = "0501";
        int endYear = 2020;
        String endMonthDay = "0301";
        Criteria criteria = new Criteria();
        List<Criteria> criteriaList = new ArrayList<>();
        //查询基态数据，且最多跨一年的情况
        criteriaList.add(Criteria.where("monthDay").gte(beginMonthDay));
        criteriaList.add(Criteria.where("monthDay").lte(endMonthDay));
        if (endYear > beginYear) {
            //跨年的情况
            criteria.orOperator(criteriaList.toArray(new Criteria[0]));
        } else {
            //不跨年的情况
            criteria.andOperator(criteriaList.toArray(new Criteria[0]));
        }
        GroupOperation group = group("gridDataList.gridLat", "gridDataList.gridLon")
                .first("gridDataList.gridLat").as("gridLat")
                .first("gridDataList.gridLon").as("gridLon")
                .sum("gridDataList.value").as("value");
        //获取资料根据起报时间的索引
        TypedAggregation<GridBaseGroundState> aggregation = newAggregation(
                GridBaseGroundState.class,
                unwind("gridDataList"),
                match(criteria),
                group);
        return mongoTemplate.aggregate(aggregation, getMongoGridGroundCollectionName("DERF_DAILY", "PRE"),
                GridData.class).getMappedResults();
    }
}
